package com.warm.common.word;

import com.warm.common.util.QRCodeUtil;
import freemarker.template.Configuration;
import freemarker.template.Template;
import org.springframework.util.CollectionUtils;
import org.springframework.util.FileCopyUtils;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Base64;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

public class WordUtil {
    // 初始化配置
    private static Configuration configuration;

    // 分隔符
    private static String separator = "/";

    public static void init(String templatePath){
        File baseDir = new File(templatePath);
        if(!baseDir.exists()){
            baseDir.mkdirs();
        }
        // 创建配置实例
        Configuration ins = new Configuration(Configuration.VERSION_2_3_0);
        // 设置编码
        ins.setDefaultEncoding("utf-8");
        // 设置默认数字格式
        ins.setNumberFormat("0.##");
        try {
            ins.setDirectoryForTemplateLoading(new File(templatePath));
        } catch (IOException e) {
            e.printStackTrace();
        }

        configuration = ins;
    }

    public static void exportDocx(Word word, String targetFilePath) {
        init(word.getTemplatePath()); // 初始化配置

        Word.Config config = word.getConfig();
        ZipOutputStream zipout = null;
        OutputStream os = null;
        try {
            os = new FileOutputStream(targetFilePath);
            zipout = new ZipOutputStream(os);
            File templateSourceFile = new File(config.getTemplateFilePath());

            ZipFile zipFile = new ZipFile(templateSourceFile);

            // 把模板中其他的文件 复制到zipout流中, 填充图片、文本内容
            coverFile(zipFile, zipout, word);

            if(word.getImageList() != null){ // 把图片写入zipout流中指定路径
                writeImage(zipout, word);
            }
            // return os.toByteArray(); // 返回字节数组
        } catch (Exception e) {
            System.out.println("word导出失败:");
        }finally {
            closeZipStream(zipout);
            closeOutStream(os);
        }
    }

    private static void writeImage(ZipOutputStream zipout, Word word) throws IOException {
        int len = -1;
        byte[] buffer = new byte[1024];
        List<Word.Image> imageList = word.getImageList();
        if (!CollectionUtils.isEmpty(imageList)){
            ZipEntry next = null;
            for (Word.Image image : imageList){
                next = new ZipEntry("word" + separator + "media" + separator + image.getFilename());
                zipout.putNextEntry(new ZipEntry(next.toString()));
                InputStream in = null;
                if(Word.ImageType.FILE.getType().equals(image.getType())){
                    in = file2InputStream((String) image.getData());
                } else if(Word.ImageType.URL.getType().equals(image.getType())){
                    in = url2InputStream((String) image.getData());
                } else if(Word.ImageType.STREAM.getType().equals(image.getType())){
                    in = (InputStream) image.getData();
                } else if(Word.ImageType.BASE64.getType().equals(image.getType())){
                    in = new ByteArrayInputStream(Base64.getDecoder().decode((String) image.getData()));
                } else if(Word.ImageType.QRCODE.getType().equals(image.getType())){
                    in = QRCodeUtil.encode((String) image.getData(), null);;
                }
                while ((len = in.read(buffer)) != -1) {
                    zipout.write(buffer, 0, len);
                }
                in.close();
            }
        }
    }

    private static void coverFile(ZipFile zipFile, ZipOutputStream zipout, Word word) throws IOException {
        Enumeration<? extends ZipEntry> zipEntrys = zipFile.entries();
        //图片配置文件模板
        ByteArrayInputStream documentXmlRelsInput = null;
        if(word.getConfig().getDocumentRelsFileName() != null){
            documentXmlRelsInput = getFreemarkerContentInputStream(word, word.getConfig().getDocumentRelsFileName());
        }
        //内容模板
        ByteArrayInputStream documentInput = null;
        if(word.getConfig().getDocumentFileName() != null){
            documentInput = getFreemarkerContentInputStream(word, word.getConfig().getDocumentFileName());
        }

        int len = -1;
        byte[] buffer = new byte[1024];
        //开始覆盖文档
        while (zipEntrys.hasMoreElements()) {
            ZipEntry next = zipEntrys.nextElement();
            // if(next.toString().indexOf("media") > 0) continue; // 图片文件夹直接跳出, 现在保留原图片文件

            zipout.putNextEntry(new ZipEntry(next.getName()));
            if("word/_rels/document.xml.rels".equals(next.getName())){ // 图片
                if (documentXmlRelsInput != null) {
                    while ((len = documentXmlRelsInput.read(buffer)) != -1) {
                        zipout.write(buffer, 0, len);
                    }
                    documentXmlRelsInput.close();
                }
            } else if("word/document.xml".equals(next.getName())){ // 文本
                if(documentInput != null){
                    while ((len = documentInput.read(buffer)) != -1) {
                        zipout.write(buffer, 0, len);
                    }
                    documentInput.close();
                }
            } else { // 其他文件
                InputStream is = zipFile.getInputStream(next);
                while ((len = is.read(buffer)) != -1) {
                    zipout.write(buffer, 0, len);
                }
                is.close();
            }
        }
    }


    /**
     * 渲染模板
     * @param word   参数
     * @param templateName  模板名称
     * @return
     */
    public static ByteArrayInputStream getFreemarkerContentInputStream(Object word, String templateName) {
        ByteArrayInputStream in = null;
        try {
            Template template = configuration.getTemplate(templateName);
            StringWriter swriter = new StringWriter();

            template.process(word, swriter);
            in = new ByteArrayInputStream(swriter.toString().getBytes("utf-8"));//这里一定要设置utf-8编码 否则导出的word中中文会是乱码
        } catch (Exception e) {
            System.out.println("模板生成错误！");
        }
        return in;
    }

    public static InputStream file2InputStream(String filepath){
        InputStream in = null;
        try {
            File file = new File(filepath);
            if(file.exists()){
                in = new FileInputStream(file);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        return in;
    }

    public static InputStream url2InputStream(String fileUrl){
        InputStream in = null;
        try {
            URL url = new URL(fileUrl);
            in = url.openStream();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return in;
    }

    public static String url2Base64Str(String fileUrl){
        HttpURLConnection conn = null;
        InputStream inputStream = null;
        try {
            conn = (HttpURLConnection) new URL(fileUrl).openConnection();
            conn.setRequestMethod("GET");
            conn.setConnectTimeout(5000);
            inputStream = conn.getInputStream();
            return Base64.getEncoder().encodeToString(FileCopyUtils.copyToByteArray(inputStream));
        } catch (Exception e) {
            System.out.println(e.getMessage());
        } finally {
            closeInStream(inputStream);
            if(conn != null) {
                conn.disconnect();
            }
        }
        return null;
    }

    private static void closeZipStream(ZipOutputStream zipout){
        if(zipout == null) return;
        try {
            zipout.close();
        } catch (IOException e) {
            System.out.println("io异常");
        }
    }

    private static void closeOutStream(OutputStream os){
        if(os == null) return;
        try {
            os.close();
        } catch (IOException e) {
            System.out.println("io异常");
        }
    }

    private static void closeInStream(InputStream in){
        if(in == null) return;
        try {
            in.close();
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }
}
